import Vue,{CreateElement,PropOptions,PropType,VNode} from 'vue';
import 'tippy.js/animations/perspective.css';
import Tippy from '../tippy';
import Icon from '../icon';
import {PlainObject,AppendTo,StandardOption,SizeObj} from '../../../../types';
import {QVirtualScroll} from 'quasar';
/**
 * 选择器形状
 */
export type ShapeType='square'|'round'|'noBorder';
/**
 * 选中前置处理器
 * @param {NodeScope} scope	节点信息
 * @return {boolean|Promise<boolean>}	是否禁止选中
 */
export interface BeforeSelected{
	(option:PlainObject):boolean|Promise<boolean>
}
/**
 * 选择器
 * @author 唐国雄
 * @date 2021/6/16
 * 属性----------------------
 * v-model			当前值, any
 * :shape			外形, ShapeType
 * :options			选项, Array<PlainObject|string>
 * :option-value	值属性, string
 * :option-label	显示属性, string
 * :emit-option		发送整个option对象, boolean
 * :disable			禁用, boolean
 * :clearable		可清空, boolean
 * :append-to		下拉框绑定位置, AppendTo
 * :before-selected	选中前置处理器, BeforeSelected
 * :filterable		可搜索, boolean
 * 事件----------------------
 * @input			值改变
 * @show			开始显示
 * @hide			开始隐藏
 */
export default Vue.extend({
	name:'Select',
	props:{
		//当前值
		value:{
			required:true,
		} as PropOptions<any>,
		//外形
		shape:{
			type:String,
			default:'square',
			validator(v){
				return [
					'square',
					'round',
					'noBorder',
				].indexOf(v)!==-1;
			}
		} as PropOptions<ShapeType>,
		//选项
		options:{
			type:Array,
			default(){
				return [];
			}
		} as PropOptions<Array<PlainObject|string>>,
		//值属性
		optionValue:{
			type:String,
			default:'value'
		},
		//显示属性
		optionLabel:{
			type:String,
			default:'label'
		},
		//发送整个option对象
		emitOption:Boolean,
		//禁用
		disable:Boolean,
		//可清空
		clearable:Boolean,
		//下拉框绑定位置, 默认document.body
		appendTo:{
			type:[String,Element,Function],
			validator(v){
				if(typeof v==='string'){
					return v==='parent';
				}
				return true;
			}
		} as PropOptions<AppendTo>,
		//选中前置处理器
		beforeSelected:Function as PropType<BeforeSelected>,
		//可搜索
		filterable:Boolean,
		//占位文本
		placeholder:{
			type:String,
			default:'请选择',
		},
	},
	data(){
		return {
			//组件宽度
			clientWidth:0,
			//是否聚焦选择器
			focus:false,
			inputFocus:false,
			//过滤值
			filter:null as null|string,
			filterConfirm:'',
			scrollTarget:null,
		};
	},
	computed:{
		classes():string{
			return 'lexmis-Select'+
				` lexmis--${this.shape}`+
				(this.focus?' lexmis--focus':'')+
				(this.disable?' lexmis--disable':'')+
				(this.clearable&&!this.disable&&this.value!=null?' lexmis--clearable':'');
		},
		//标准化后的选项
		transferOptions():Array<StandardOption>{
			const {optionValue,optionLabel}=this;
			return this.options.map(function(option:PlainObject|string){
				if(typeof option==='string'){
					return {label:option,value:option,option};
				}
				return {
					label:option[optionLabel],
					value:option[optionValue],
					option,
				};
			});
		},
		showTransferOptions():Array<StandardOption>{
			let filterConfirm=this.filterConfirm;
			if(filterConfirm){
				filterConfirm=filterConfirm.toLocaleLowerCase();
				return this.transferOptions.filter(({label}) => {
					return label.toLocaleLowerCase().indexOf(filterConfirm)!==-1;
				});
			}
			return this.transferOptions;
		},
		//当前选中的选项
		curOption():StandardOption|null{
			const {transferOptions,emitOption,optionValue,value}=this;
			for(let i=0;i<transferOptions.length;++i){
				if(
					emitOption&&typeof value!=='string'
						?value!=null&&transferOptions[i].value===value[optionValue]
						:transferOptions[i].value===value
				){
					return transferOptions[i];
				}
			}
			return null;
		},
		//显示值
		showValue():string{
			if(this.curOption){
				return this.curOption.label;
			}
			return '';
		},
		//传给Tippy组件的属性
		tippyOptions():PlainObject{
			return {
				advanced:true,
				head:false,
				disable:this.disable,
				appendTo:this.appendTo,
				width:0,
				minWidth:this.clientWidth,
				'tippy-options':{
					arrow:false,
					placement:'bottom-start',
					duration:[300,300],
					animation:'perspective',
					offset:[0,5],
					theme:'lexmis-widget',
				}
			};
		},
	},
	methods:{
		$_lexmis_show(){
			this.focus=true;
			this.clientWidth=this.$el.clientWidth;
			const filter=this.filter||'';
			if(this.filterConfirm!==filter){
				this.filterConfirm=filter;
			}
			this.$emit('show');
			this.$nextTick(function(){
				// @ts-ignore
				this.scrollTarget=this.$refs.Tippy.$refs.scroll.getVScrollTarget();
			});
		},
		$_lexmis_hide(){
			this.focus=false;
			if(!this.inputFocus){
				this.filter=null;
			}
			this.$emit('hide');
		},
		$_lexmis_renderContent(h:CreateElement,{height}:SizeObj){
			const contentItem=(option:StandardOption,index:number) => {
				return h('div',{
					staticClass:'lexmis_option'+
						(this.curOption===option?' lexmis_select':''),
					key:index,
					on:{
						click:() => {
							if(this.curOption!==option){
								const beforeSelected=this.beforeSelected;
								Promise.resolve(beforeSelected&&beforeSelected({option})).then((interrupt) => {
									if(!interrupt){
										this.$emit(
											'input',
											this.emitOption
												?option.option
												:option.value
										);
									}
								});
							}
							// @ts-ignore
							this.$refs.Tippy.hide();
						}
					}
				},option.label);
			};
			let content;
			if(this.options.length>=100){
				content=h(QVirtualScroll,{
					staticClass:'lexmis_Select_options',
					staticStyle:{
						minWidth:this.clientWidth+'px',
					},
					ref:'virtualScroll',
					props:{
						scrollTarget:this.scrollTarget,
						virtualScrollSliceSize:Math.ceil(height/30*2),
						virtualScrollItemSize:30,
						items:this.showTransferOptions,
					},
					scopedSlots:{
						default:({item,index}) => contentItem(item,index)
					}
				});
			}else{
				content=h('div',{
					staticClass:'lexmis_Select_options',
					staticStyle:{
						minWidth:this.clientWidth+'px',
					}
				},this.showTransferOptions.map(contentItem));
			}
			return this.showTransferOptions.length
				?content
				:h('div',{
					staticClass:'lexmis_nodata',
				},this.transferOptions.length?'无匹配数据':'暂无数据');
		},
	},
	render(h):VNode{
		return h(Tippy,{
			ref:'Tippy',
			staticClass:'lexmis_Select_Tippy',
			props:this.tippyOptions,
			on:{
				show:this.$_lexmis_show,
				hide:this.$_lexmis_hide,
			},
			scopedSlots:{
				content:(props) => this.$_lexmis_renderContent(h,props),
				default:() => h('div',{
					staticClass:this.classes,
					attrs:{
						tabindex:this.disable?-1:0,
					},
					on:{
						keydown:(e:KeyboardEvent) => {
							if(e.key==='Enter'){
								if(this.focus){
									// @ts-ignore
									this.$refs.Tippy.hide();
								}else{
									// @ts-ignore
									this.$refs.Tippy.show();
								}
							}
						},
					}
				},[
					h('div',{
						staticClass:'lexmis_content',
					},[
						this.showValue||this.placeholder,
						h('input',{
							attrs:{
								readonly:!this.filterable,
								tabindex:-1,
							},
							domProps:{
								value:this.filter==null?this.showValue:this.filter,
								type:'text',
								placeholder:this.placeholder,
								disabled:this.disable,
							},
							on:{
								input:(e:InputEvent) => {
									const inputVal=(e.target as HTMLInputElement).value;
									if((this.filter==null?this.showValue:this.filter)!==inputVal){
										this.filterConfirm=this.filter=inputVal;
										if(!this.focus){
											// @ts-ignore
											this.$refs.Tippy.show();
										}
									}
								},
								focus:() => {
									this.inputFocus=true;
								},
								blur:() => {
									this.inputFocus=false;
									if(!this.focus){
										this.filter=null;
									}
								},
							}
						})
					]),
					h(Icon,{
						staticClass:'lexmis_arrow',
						props:{
							name:'play_arrow'
						}
					}),
					this.clearable
						?h(Icon,{
							staticClass:'lexmis_clearable',
							props:{
								name:'cancel'
							},
							on:{
								click:(e:Event) => {
									e.stopPropagation();
									if(this.value!=null){
										const beforeSelected=this.beforeSelected;
										Promise.resolve(beforeSelected&&beforeSelected({})).then((interrupt) => {
											if(!interrupt){
												this.$emit('input',null);
											}
										});
									}
								}
							}
						})
						:null,
				])
			}
		});
	},
});
